package org.convallaria.framework.security.service;

import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.convallaria.framework.security.config.SecurityProperties;
import org.convallaria.framework.security.core.SecurityUser;
import org.convallaria.infrastruct.ce.util.CacheKeyGenerator;
import org.convallaria.infrastruct.ce.util.CacheUtils;
import org.convallaria.infrastruct.ce.util.RedisCacheUtils;
import org.springframework.stereotype.Service;

import java.time.Duration;

/**
 * Token服务
 * 负责token的存储、验证、刷新等操作
 * 
 * @author convallaria
 * @since 1.0.0
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class TokenService {

    private final SecurityProperties securityProperties;
    private final RedisCacheUtils redisCacheUtils;
    private final CacheUtils cacheUtils;
    private final CacheKeyGenerator keyGenerator;

    // 缓存配置常量
    private static final String TOKEN_CACHE_NAME = "security:token";
    private static final String USER_TOKEN_CACHE_NAME = "security:user:token";
    private static final String REFRESH_TOKEN_CACHE_NAME = "security:refresh:token";
    
    // 键前缀
    private static final String TOKEN_PREFIX = "token:";
    private static final String USER_TOKEN_PREFIX = "user:token:";
    private static final String REFRESH_TOKEN_PREFIX = "refresh:token:";

    /**
     * 存储token
     * 
     * @param token JWT token
     * @param securityUser 用户信息
     */
    public void storeToken(String token, SecurityUser securityUser) {
        try {
            // 生成缓存键
            String tokenKey = keyGenerator.generateKey(TOKEN_PREFIX, token);
            String userTokenKey = keyGenerator.generateKey(USER_TOKEN_PREFIX, securityUser.getUserId().toString());
            
            // 使用RedisCacheUtils存储token信息（主要存储）
            redisCacheUtils.set(tokenKey, securityUser, Duration.ofMinutes(securityProperties.getJwt().getExpireMinutes()));
            
            // 使用CacheUtils存储到通用缓存（用于统计和监控）
            cacheUtils.put(TOKEN_CACHE_NAME, tokenKey, securityUser);
            
            // 处理多终端登录
            if (!securityProperties.getJwt().isMultiLogin()) {
                // 不支持多终端登录，需要删除之前的token
                removeUserTokens(securityUser.getUserId());
            } else {
                // 支持多终端登录，但需要限制设备数
                limitUserDevices(securityUser.getUserId(), token);
            }
            
            // 记录用户token映射
            redisCacheUtils.set(userTokenKey, token, Duration.ofMinutes(securityProperties.getJwt().getExpireMinutes()));
            cacheUtils.put(USER_TOKEN_CACHE_NAME, userTokenKey, token);
            
            log.info("Token存储成功: userId={}, token={}", securityUser.getUserId(), token);
            
        } catch (Exception e) {
            log.error("存储Token失败: token={}", token, e);
            throw new RuntimeException("存储Token失败", e);
        }
    }

    /**
     * 验证token是否有效
     * 
     * @param token JWT token
     * @return 是否有效
     */
    public boolean validateToken(String token) {
        if (token == null || token.trim().isEmpty()) {
            return false;
        }
        
        try {
            String tokenKey = keyGenerator.generateKey(TOKEN_PREFIX, token);
            return redisCacheUtils.exists(tokenKey);
        } catch (Exception e) {
            log.error("验证Token失败: token={}", token, e);
            return false;
        }
    }

    /**
     * 删除token
     * 
     * @param token JWT token
     */
    public void removeToken(String token) {
        if (token == null || token.trim().isEmpty()) {
            return;
        }
        
        try {
            String tokenKey = keyGenerator.generateKey(TOKEN_PREFIX, token);
            SecurityUser user = redisCacheUtils.get(tokenKey, SecurityUser.class);
            
            if (user != null) {
                // 删除token
                redisCacheUtils.delete(tokenKey);
                cacheUtils.evict(TOKEN_CACHE_NAME, tokenKey);
                
                // 从用户token集合中移除
                String userTokenKey = keyGenerator.generateKey(USER_TOKEN_PREFIX, user.getUserId().toString());
                redisCacheUtils.delete(userTokenKey);
                cacheUtils.evict(USER_TOKEN_CACHE_NAME, userTokenKey);
                
                log.info("Token删除成功: userId={}, token={}", user.getUserId(), token);
            }
        } catch (Exception e) {
            log.error("删除Token失败: token={}", token, e);
        }
    }

    /**
     * 删除用户所有token
     * 
     * @param userId 用户ID
     */
    public void removeUserTokens(Long userId) {
        if (userId == null) {
            return;
        }
        
        try {
            String userTokenKey = keyGenerator.generateKey(USER_TOKEN_PREFIX, userId.toString());
            String token = redisCacheUtils.get(userTokenKey, String.class);
            
            if (token != null) {
                // 删除token
                String tokenKey = keyGenerator.generateKey(TOKEN_PREFIX, token);
                redisCacheUtils.delete(tokenKey);
                cacheUtils.evict(TOKEN_CACHE_NAME, tokenKey);
                
                // 删除用户token映射
                redisCacheUtils.delete(userTokenKey);
                cacheUtils.evict(USER_TOKEN_CACHE_NAME, userTokenKey);
                
                log.info("用户所有Token删除成功: userId={}", userId);
            }
        } catch (Exception e) {
            log.error("删除用户Token失败: userId={}", userId, e);
        }
    }

    /**
     * 创建刷新token
     * 
     * @param userId 用户ID
     * @return 刷新token
     */
    public String createRefreshToken(Long userId) {
        try {
            String refreshToken = UUID.randomUUID().toString().replace("-", "");
            String refreshTokenKey = keyGenerator.generateKey(REFRESH_TOKEN_PREFIX, refreshToken);
            
            // 使用RedisCacheUtils存储刷新token
            redisCacheUtils.set(refreshTokenKey, userId, Duration.ofMinutes(securityProperties.getJwt().getRefreshMinutes()));
            
            // 使用CacheUtils存储到通用缓存
            cacheUtils.put(REFRESH_TOKEN_CACHE_NAME, refreshTokenKey, userId);
            
            log.info("刷新Token创建成功: userId={}, refreshToken={}", userId, refreshToken);
            return refreshToken;
            
        } catch (Exception e) {
            log.error("创建刷新Token失败: userId={}", userId, e);
            throw new RuntimeException("创建刷新Token失败", e);
        }
    }

    /**
     * 验证刷新token
     * 
     * @param refreshToken 刷新token
     * @return 用户ID
     */
    public Long validateRefreshToken(String refreshToken) {
        if (refreshToken == null || refreshToken.trim().isEmpty()) {
            return null;
        }
        
        try {
            String refreshTokenKey = keyGenerator.generateKey(REFRESH_TOKEN_PREFIX, refreshToken);
            return redisCacheUtils.get(refreshTokenKey, Long.class);
        } catch (Exception e) {
            log.error("验证刷新Token失败: refreshToken={}", refreshToken, e);
            return null;
        }
    }

    /**
     * 删除刷新token
     * 
     * @param refreshToken 刷新token
     */
    public void removeRefreshToken(String refreshToken) {
        if (refreshToken == null || refreshToken.trim().isEmpty()) {
            return;
        }
        
        try {
            String refreshTokenKey = keyGenerator.generateKey(REFRESH_TOKEN_PREFIX, refreshToken);
            redisCacheUtils.delete(refreshTokenKey);
            cacheUtils.evict(REFRESH_TOKEN_CACHE_NAME, refreshTokenKey);
            
            log.info("刷新Token删除成功: refreshToken={}", refreshToken);
        } catch (Exception e) {
            log.error("删除刷新Token失败: refreshToken={}", refreshToken, e);
        }
    }

    /**
     * 限制用户设备数
     * 
     * @param userId 用户ID
     * @param newToken 新token
     */
    private void limitUserDevices(Long userId, String newToken) {
        try {
            String userTokenKey = keyGenerator.generateKey(USER_TOKEN_PREFIX, userId.toString());
            String existingToken = redisCacheUtils.get(userTokenKey, String.class);
            
            if (existingToken != null) {
                // 删除旧token
                String oldTokenKey = keyGenerator.generateKey(TOKEN_PREFIX, existingToken);
                redisCacheUtils.delete(oldTokenKey);
                cacheUtils.evict(TOKEN_CACHE_NAME, oldTokenKey);
                
                log.info("设备数限制，删除旧Token: userId={}, oldToken={}", userId, existingToken);
            }
        } catch (Exception e) {
            log.error("限制用户设备数失败: userId={}", userId, e);
        }
    }

    /**
     * 延长token有效期
     * 
     * @param token JWT token
     */
    public void extendTokenExpiration(String token) {
        if (token == null || token.trim().isEmpty()) {
            return;
        }
        
        try {
            String tokenKey = keyGenerator.generateKey(TOKEN_PREFIX, token);
            SecurityUser user = redisCacheUtils.get(tokenKey, SecurityUser.class);
            
            if (user != null) {
                // 延长token过期时间
                redisCacheUtils.expire(tokenKey, Duration.ofMinutes(securityProperties.getJwt().getExpireMinutes()));
                
                // 延长用户token集合的过期时间
                String userTokenKey = keyGenerator.generateKey(USER_TOKEN_PREFIX, user.getUserId().toString());
                redisCacheUtils.expire(userTokenKey, Duration.ofMinutes(securityProperties.getJwt().getExpireMinutes()));
                
                log.info("Token过期时间延长成功: userId={}, token={}", user.getUserId(), token);
            }
        } catch (Exception e) {
            log.error("延长Token过期时间失败: token={}", token, e);
        }
    }

    /**
     * 获取token对应的用户信息
     * 
     * @param token JWT token
     * @return 用户信息
     */
    public SecurityUser getTokenUser(String token) {
        if (token == null || token.trim().isEmpty()) {
            return null;
        }
        
        try {
            String tokenKey = keyGenerator.generateKey(TOKEN_PREFIX, token);
            return redisCacheUtils.get(tokenKey, SecurityUser.class);
        } catch (Exception e) {
            log.error("获取Token用户信息失败: token={}", token, e);
            return null;
        }
    }

    /**
     * 获取用户当前token
     * 
     * @param userId 用户ID
     * @return 当前token
     */
    public String getUserCurrentToken(Long userId) {
        if (userId == null) {
            return null;
        }
        
        try {
            String userTokenKey = keyGenerator.generateKey(USER_TOKEN_PREFIX, userId.toString());
            return redisCacheUtils.get(userTokenKey, String.class);
        } catch (Exception e) {
            log.error("获取用户当前Token失败: userId={}", userId, e);
            return null;
        }
    }

    /**
     * 检查用户是否有有效token
     * 
     * @param userId 用户ID
     * @return 是否有有效token
     */
    public boolean hasValidToken(Long userId) {
        if (userId == null) {
            return false;
        }
        
        try {
            String userTokenKey = keyGenerator.generateKey(USER_TOKEN_PREFIX, userId.toString());
            String token = redisCacheUtils.get(userTokenKey, String.class);
            
            if (token != null) {
                return validateToken(token);
            }
            return false;
        } catch (Exception e) {
            log.error("检查用户有效Token失败: userId={}", userId, e);
            return false;
        }
    }

    /**
     * 清理过期的token
     * 
     * @return 清理的数量
     */
    public long cleanExpiredTokens() {
        try {
            // 清理过期的token
            String tokenPattern = TOKEN_PREFIX + "*";
            long tokenCleanedCount = redisCacheUtils.deleteByPattern(tokenPattern);
            
            // 清理过期的用户token映射
            String userTokenPattern = USER_TOKEN_PREFIX + "*";
            long userTokenCleanedCount = redisCacheUtils.deleteByPattern(userTokenPattern);
            
            // 清理过期的刷新token
            String refreshTokenPattern = REFRESH_TOKEN_PREFIX + "*";
            long refreshTokenCleanedCount = redisCacheUtils.deleteByPattern(refreshTokenPattern);
            
            // 清理通用缓存
            cacheUtils.clear(TOKEN_CACHE_NAME);
            cacheUtils.clear(USER_TOKEN_CACHE_NAME);
            cacheUtils.clear(REFRESH_TOKEN_CACHE_NAME);
            
            long totalCleaned = tokenCleanedCount + userTokenCleanedCount + refreshTokenCleanedCount;
            log.info("清理过期Token完成，共清理 {} 个", totalCleaned);
            
            return totalCleaned;
        } catch (Exception e) {
            log.error("清理过期Token失败", e);
            return 0;
        }
    }

    /**
     * 获取token统计信息
     * 
     * @return 统计信息
     */
    public TokenStatistics getTokenStatistics() {
        try {
            // 统计各种token数量
            long tokenCount = redisCacheUtils.size(TOKEN_PREFIX + "*");
            long userTokenCount = redisCacheUtils.size(USER_TOKEN_PREFIX + "*");
            long refreshTokenCount = redisCacheUtils.size(REFRESH_TOKEN_PREFIX + "*");
            
            // 获取缓存统计信息
            CacheUtils.CacheStatistics tokenCacheStats = cacheUtils.getStatistics(TOKEN_CACHE_NAME);
            CacheUtils.CacheStatistics userTokenCacheStats = cacheUtils.getStatistics(USER_TOKEN_CACHE_NAME);
            CacheUtils.CacheStatistics refreshTokenCacheStats = cacheUtils.getStatistics(REFRESH_TOKEN_CACHE_NAME);
            
            return TokenStatistics.builder()
                    .activeTokenCount(tokenCount)
                    .userTokenCount(userTokenCount)
                    .refreshTokenCount(refreshTokenCount)
                    .totalTokenCount(tokenCount + userTokenCount + refreshTokenCount)
                    .tokenCacheSize(tokenCacheStats.getSize())
                    .userTokenCacheSize(userTokenCacheStats.getSize())
                    .refreshTokenCacheSize(refreshTokenCacheStats.getSize())
                    .build();
                    
        } catch (Exception e) {
            log.error("获取Token统计信息失败", e);
            return TokenStatistics.empty();
        }
    }

    /**
     * Token统计信息
     */
    @lombok.Data
    @lombok.Builder
    public static class TokenStatistics {
        private long activeTokenCount;
        private long userTokenCount;
        private long refreshTokenCount;
        private long totalTokenCount;
        private long tokenCacheSize;
        private long userTokenCacheSize;
        private long refreshTokenCacheSize;

        public static TokenStatistics empty() {
            return TokenStatistics.builder().build();
        }
    }
}